/*
@STI LICENSE
(C) 2006-2008 Universit�t Innsbruck.

This file is part of myOntology.

myOntologyis free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

myOntologyis distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with myOntology.  If not, see <http://www.gnu.org/licenses/>.

@STI LICENSE */
package controller.sesameDataConnector;

import java.io.File;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;

import model.Vocabulary;
import model.dataContainer.AbstractElement;
import model.dataContainer.ClassContainer;
import model.dataContainer.DatatypePropertyContainer;
import model.dataContainer.ObjectPropertyContainer;
import model.dataContainer.OntologyModuleContainer;
import model.dataContainer.TranslationContainer;

import org.apache.log4j.Logger;
import org.openrdf.model.Literal;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.impl.LiteralImpl;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.model.vocabulary.RDFS;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.RepositoryResult;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.sail.nativerdf.NativeStore;


public abstract class AbstractDataConnector {

	private static Repository myRepository;

	private static String sesameDataDirectory="C:/DevelopmentRepository1";
	
	public static Logger logger = Logger.getLogger(AbstractDataConnector.class);
	
	
	public static void SetSesameDataDirectory( String entry ) {
		AbstractDataConnector.sesameDataDirectory = entry;
	}
	
	public static URI createURI( AbstractElement data ) {

		URI ret = null;
		ValueFactory myFactory = getRepository().getValueFactory();

		//data.setLabel(java.net.URLEncoder.encode(data.getLabel(), "UTF-8"));
		// generate unique uri for the ontology
		if (data instanceof OntologyModuleContainer)
			ret = myFactory.createURI(Vocabulary.ONTOLOGYMODULE_NAMESPACE.stringValue() + data.getLabel());
		else if (data instanceof ClassContainer)
			ret = myFactory.createURI(Vocabulary.CLASS_NAMESPACE.stringValue() + data.getLabel());
		else if (data instanceof DatatypePropertyContainer || data instanceof ObjectPropertyContainer) 
			ret = myFactory.createURI(Vocabulary.PROPERTY_NAMESPACE.stringValue() + data.getLabel());
			
		return ret;
	}
	

	public static URI createURI( URI type, String label ) {

		URI ret = null;
		
		while( label.contains( "  " ) ) {
			label = label.replace( "  ", " " );
		}
		
		label = label.replace( " ", "_");
		
		// Encode special characters:
		try {
			
			// Encode special characters:
			label = URLEncoder.encode( label, "UTF8" );			

			if( type.equals( Vocabulary.ONTOLOGYMODULE )) 
				ret = new URIImpl( Vocabulary.ONTOLOGYMODULE_NAMESPACE.stringValue() + label );
			else if( type.equals( Vocabulary.CLASS )) 
				ret = new URIImpl( Vocabulary.CLASS_NAMESPACE.stringValue() + label );
			else if( type.equals( Vocabulary.DATATYPEPROPERTY ) || type.equals( Vocabulary.OBJECTPROPERTY ) )
				ret = new URIImpl( Vocabulary.PROPERTY_NAMESPACE.stringValue() + label );
			
		}
		catch( Exception e ) {
			
		}


		return ret;
	}

	/**
	 * if repository was initialized this mehtod initializes one, else it returns the already initialized repository
	 * @return a repository
	 */
	private static Repository getRepository() {
		
		// generate object "myRepository" just 1 time and use it in future.
		if( myRepository == null ) {
		
			logger.info( "Try to set up a connection to repository." );
			try{

				/* HTTP-Repository has a speed-down from about 50% compared to NativeStore. Faster would be MemoryStore but has the disadvantage of limitation to RAM-space for the store. Natice scales better.
				String repositoryID = "myontology";

				myRepository = new HTTPRepository( "http://localhost:8080/openrdf-sesame/", repositoryID);
				myRepository.initialize();
				*/				
				
				File f = new File(".");

				logger.info("DataDir: "+sesameDataDirectory);
				java.io.File dataDir = new java.io.File( AbstractDataConnector.sesameDataDirectory ); //"C:\\Dokumente und Einstellungen\\andklo\\Application Data\\Aduna\\OpenRDF Sesame\\repositories\\myontology");
				NativeStore store = new NativeStore(dataDir); // nativestore stores its data on harddisk
				// old (6.3.08): spoc, posc, cosp
				store.setTripleIndexes( "spoc,posc,opsc" ); // subject predicate object, ... for faster querying (but slower insterting because of lots of indexes)
				// documentation: see http://www.openrdf.org/doc/sesame2/users/ch03.html#d0e246
				
				
				
				/*
				java.io.File dataDir = new java.io.File( AbstractDataConnector.sesameDataDirectory );
				MemoryStore store = new MemoryStore(dataDir);
				store.setSyncDelay(1000L);
				*/
				
				myRepository = new SailRepository( store );
				myRepository.initialize();			

			} catch ( Exception e )	{
				logger.error( "Error during setting up a connection to repository: " ,e );

			}
		}
		
		return myRepository;
	}
	
	public static RepositoryConnection getConnection() throws RepositoryException {
		
		RepositoryConnection ret = getRepository().getConnection();
		ret.setAutoCommit( false );
		
		return ret;
	}
	public static RepositoryConnection getStandardConnection() throws RepositoryException {
		
		RepositoryConnection ret = getRepository().getConnection();
		
		return ret;
	}


	/**
	 * Selects count elements of type "type", which contain the string "input" 
	 * @param type
	 * @param input
	 * @return
	 */
	public static ArrayList<URI> getMatchingElements( URI type, String input, int count ) {

		ArrayList<URI> list = null;	
		
		try{
			RepositoryConnection con = getRepository().getConnection();
			try {//if( myRepository == null )
				list = new ArrayList<URI>(); // will be set to null if contains no element 
				//select all ontologies
				String query = "";

				// ########################## LABEL ##################################
				
				// Matching elements (non case sensitive)
				query = "SELECT DISTINCT x FROM {x} <" + RDF.TYPE + "> {<" + type.stringValue() + ">}; <" + RDFS.LABEL + "> {l}" +
						" WHERE l LIKE \"" + input + "\" IGNORE CASE" +
						" LIMIT " + count;
				
				fillFoundIfNotIncluded( list, query, con );	

				
				// elements starting with the input (non case sensitive)
				if( list.size() < count ) {
					
					query = "SELECT DISTINCT x FROM {x} <" + RDF.TYPE + "> {<" + type.stringValue() + ">}; <" + RDFS.LABEL + "> {l}" +
					" WHERE l LIKE \"" + input + "*\" IGNORE CASE AND NOT l LIKE \"" + input + "\" IGNORE CASE" +
					" LIMIT " + (count - list.size());

					fillFoundIfNotIncluded( list, query, con );	
	
					// elements having the input anywhere (non case sensitive)
					if( list.size() < count ) {
						
						query = "SELECT DISTINCT x FROM {x} <" + RDF.TYPE + "> {<" + type.stringValue() + ">}; <" + RDFS.LABEL + "> {l}" +
						" WHERE l LIKE \"*" + input + "*\" IGNORE CASE AND NOT l LIKE \"" + input + "\" IGNORE CASE AND NOT l LIKE \"" + input + "*\" IGNORE CASE" +
						" LIMIT " + (count - list.size());
	
						fillFoundIfNotIncluded( list, query, con );
						
						
						
						// ########################## SYNONYMS ##################################
						
						// elements having the input in the synonym (non case sensitive)
						if( list.size() < count ) {
							
							query = "SELECT DISTINCT x FROM {x} <" + RDF.TYPE + "> {<" + type.stringValue() + ">}; <" + Vocabulary.SYNONYM + "> {l}" +
									" WHERE l LIKE \"" + input + "\" IGNORE CASE" +
									" LIMIT " + count;
		
							fillFoundIfNotIncluded( list, query, con );	
						
						
							// elements starting with the input (non case sensitive)
							if( list.size() < count ) {
								
								query = "SELECT DISTINCT x FROM {x} <" + RDF.TYPE + "> {<" + type.stringValue() + ">}; <" + Vocabulary.SYNONYM + "> {l}" +
								" WHERE l LIKE \"" + input + "*\" IGNORE CASE AND NOT l LIKE \"" + input + "\" IGNORE CASE" +
								" LIMIT " + (count - list.size());
			
								fillFoundIfNotIncluded( list, query, con );	
								
				
								// elements having the input anywhere (non case sensitive)
								if( list.size() < count ) {
									
									query = "SELECT DISTINCT x FROM {x} <" + RDF.TYPE + "> {<" + type.stringValue() + ">}; <" + Vocabulary.SYNONYM + "> {l}" +
									" WHERE l LIKE \"*" + input + "*\" IGNORE CASE AND NOT l LIKE \"" + input + "\" IGNORE CASE AND NOT l LIKE \"" + input + "*\" IGNORE CASE" +
									" LIMIT " + (count - list.size());
									
									fillFoundIfNotIncluded( list, query, con );					
								}
							}
						}
					}
				}
				
				
				if( list.size()==0 )
					list = null;				
				
			} finally {
				con.close();
			}
			
		}catch (Exception e) {
			logger.error("Error occoured while selecting all " + type.stringValue() +":",e);

		}		
		
		return list;		
	}
	
	private static ArrayList<URI> fillFoundIfNotIncluded( ArrayList<URI> list, String query, RepositoryConnection con ) throws Exception {
		
		TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
		TupleQueryResult result = tupleQuery.evaluate();
		try {			
			while (result.hasNext()) {
				URI tmp = (URI)result.next().getValue("x");
				if( !list.contains( tmp ) ) {								
					list.add( tmp );
				}
			}
		} finally {
			result.close();
		}
		
		return list;
	}
	
	
	/**
	 * Selects all Subjects of the triples <subjects> <mo:hasTag> "tag"
	 * @param tag
	 * @return
	 */
	public static ArrayList<URI> selectTagElements(String tag, URI type ) {

		ArrayList<URI> list = null;
		
		try {
			RepositoryConnection con = getConnection();
			try {
				//select all ontologies

				String query = "SELECT DISTINCT x FROM {x} <" + Vocabulary.HASTAG + "> {y}, {x} <"+RDF.TYPE+"> {<" + type.stringValue() + ">} WHERE isLiteral(y) AND y=\""+ tag +"\"";
				
				
				//System.out.println(query);
				TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
				TupleQueryResult result = tupleQuery.evaluate();
				try {
					if( result.hasNext() ) {
						list = new ArrayList<URI>();
						
						while( result.hasNext() ) {
							list.add( (URI)result.next().getValue("x") );
						}
					}
				} finally {
					result.close();
				}
			} finally {
				con.close();
			}
			
		}catch (Exception e) {
			logger.error("Error occoured in selectTagElements: " ,e);

		}		
		
		return list;
	}
	
	public static boolean hasOpenChangerequest( URI id ) {
		boolean ret = false;
		if( id!=null ) {
	
			try {

				RepositoryConnection con = getRepository().getConnection();
				try {				
					RepositoryResult<Statement> statements = con.getStatements( null, Vocabulary.ISCHANGEREQUESTFOR, id, false );
					try { 
						while (ret==false && statements.hasNext()) {
							if (ChangerequestConnector.isOpen((URI)statements.next().getSubject()))
								ret = true;
						} 
					}finally {
						statements.close();
					}
				} finally {
					con.close();
				}
			} catch (Exception e) {
				
			}
		}
		return ret;
	}
	

	
	
	/**
	 * returns the URI of an element elemenId (=http://myontology.org/"elementId")
	 * @param elementId	 could be e.g. /Class/Dog
	 * @return 
	 */
	public static URI getURI( String elementId ) {
		URI ret = null;
		
		if( elementId!=null && !elementId.equals("") ) {
			
			try {
				
				// Encode special Characters
				// compare HelperFunctions.getEncodedURI()
				// 	and AbstractDataConnector.createURI() - this encodes the label to get an encoded URI
				String tmp = "";
				elementId = elementId.replace( Vocabulary.NAMESPACE.stringValue(), "" );
				
				String[] parts = elementId.split("/");
				if( parts!=null ) {
					for( int i=0; i<parts.length; i++) {
						tmp += URLEncoder.encode( parts[i], "UTF8" ) + '/';
					}
				}
				
				if( tmp.lastIndexOf( '/' )==tmp.length()-1) {
					tmp = tmp.substring(0, tmp.length()-1 );
				}
				
				ret = new URIImpl( Vocabulary.NAMESPACE.stringValue() + tmp );
			}
			catch( Exception e )
			{
				logger.error( e );
			}
		}
		
		return ret;
	}
	
	
	
	/**
	 * returns the uri of the type of the element with URI uri
	 * @return uri uri of the type of the element with URI uri
	 */
	public static URI getType( URI uri ) {
		
		URI ret = null;
		
		if( uri!=null) {
			
			try { 
				
				ret = selectObject( uri, RDF.TYPE );
			} catch( Exception e ) {
				
				logger.error(e);
			}
		}
		
		return ret;
	}
	
	
	
	public static boolean isURLreachable( URI uri ) {
		
		boolean ret = false;
		
		try {
			URL url = new URL(uri.toString());
			//url = new URL(url.getProtocol()+"://"+url.getHost());
			// System.out.println(url.toString());
			
			java.net.URLConnection u = url.openConnection();
			u.setRequestProperty( "User-Agent", "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0; H010818)" );
			u.setReadTimeout(1000);
			
			u.getInputStream().read();

			ret = true;
		} catch (Exception e) {
			logger.error("URL not reachable!",e);
		}
		
		return ret;
	}
	
	
	//-------------------------------------------------------------------------------------------
	//------------------------ delete, insert, select, update -----------------------------------
	//	----------------------------------------------------------------------------------------
	
	
	/**
	 * delete the element with the  URI uri
	 * @param uri the uri of the element to delete
	 * @return true if the element was delete 
	 * @throws ControllerException
	 */
	public static boolean delete( URI uri ) throws ControllerException {
		try{
			RepositoryConnection con = getRepository().getConnection();
						
			try {
				int i = 0;
				RepositoryResult<Statement> statements = con.getStatements(null, null, uri, false );
				try {
					if (statements.hasNext()) {
						ControllerException e = new ControllerException("There are dependencies for this Element");
						while (i < 10 && statements.hasNext()) {
							e.addURI( (URI)statements.next().getSubject() );
							i++;
						}
						throw e;
					}
				} finally {
					statements.close();
				}
				con.remove(uri, null, null);
			}finally {
				con.close();
			}
			return true;
		} catch (Exception e) {
			logger.error("Error while deleting the element " ,e);
			
			if (e instanceof ControllerException) {
				throw (ControllerException) e;
			}
			return false;
		}
	}

	
	public static boolean insertTripel (URI s, URI p, URI o) throws Exception {
		boolean ret = false;
		
		if( s!=null && p!=null && o!=null ) {
			try {
				RepositoryConnection con = getRepository().getConnection();
				
				try {
					
					// System.out.println( "insert tripel: " + s.stringValue() + " " + p.stringValue() + " " + o.stringValue());				
					
					con.add(s,p,o);
					
					con.commit();
					
					ret = true;
				} catch( Exception e ) {
					con.rollback();
					throw e;	
				} finally {
					con.close();
				}
			} catch (Exception e) {
				logger.error("Error while inserting a triple",e);				
				throw e;
			}
		}
			
		return ret;
	}
	
	public static boolean deleteTriple(URI s, URI p, URI o) throws Exception {
		boolean ret = false;
		
		try {
			RepositoryConnection con = getRepository().getConnection();
			
			try {
				con.remove(s,p,o);
				
				con.commit();
				ret = true;
			} catch( Exception e ) {
				con.rollback();	
				throw e;
			} finally {
				con.close();
			}
		} catch (Exception e) {
			logger.error("Error while deleting a triple: " + e.getMessage());
			
			throw e;
		}
			
		return ret;
	}
	
	
	/**
	 * Selects count elements of type "type", which contain the string "input" 
	 * @param type
	 * @param input
	 * @return
	 *//*
	public static ArrayList<URI> selectSubjectsWhereIdIsInObject( URI o ) {

		ArrayList<URI> list = new ArrayList<URI>();	
		
		try{
			RepositoryConnection con = getRepository().getConnection();
			try {
				//select all ontologies

				String query = "SELECT DISTINCT FROM {x} {p} {<" + o.stringValue() + ">}";
				
				// "SELECT DISTINCT x FROM {x} <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> {type}; <http://www.w3.org/2000/01/rdf-schema#label> {l} WHERE type=<http://myontology.org/v0#Class> AND l LIKE "*bs*" IGNORE CASE LIMIT 10"

				//System.out.println(query);
				TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
				TupleQueryResult result = tupleQuery.evaluate();
				try {				
					while (result.hasNext()) {
						list.add( (URI)result.next().getValue("x") );
					}
				} finally {
					result.close();
				}
			} finally {
				con.close();
			}
			
		}catch (Exception e) {
			System.out.println("Error occoured while selectSubjectsWhereIdIsInObject: " + e.getMessage());
			e.printStackTrace();
		}		
		
		return list;		
	}
	*/
	

	/**
	 * Selects all subjects, which have p in property and o in object (s p o)
	 * @param p	property
	 * @param o	object
	 * @return
	 */
	public static ArrayList<URI> selectSubjects( URI p, URI o ) {
		
		ArrayList<URI> ret = selectSubjects( p, o, null );
		
		return ret;		
	}
	
	
	
	/**
	 * Selects all subjects, which have p in property and o in object (s p o)
	 * @param p	property
	 * @param o	object
	 * @param type the type of the subjects in the result
	 * @return
	 */
	public static ArrayList<URI> selectSubjects( URI p, URI o, URI type ) {
		ArrayList<URI> ret = null;

		try{
			if( p!=null && o!=null ) {
				
				RepositoryConnection con = getRepository().getConnection();
				try {
					String query = "";
					if( type!=null )
						query = "SELECT DISTINCT x FROM {x} <" + p.stringValue() + "> {<" + o.stringValue() + ">}; <"+RDF.TYPE+"> {<" + type.stringValue() + ">}";
					else
						query = "SELECT DISTINCT x FROM {x} <" + p.stringValue() + "> {<" + o.stringValue() + ">}";
				
					TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
					TupleQueryResult result = tupleQuery.evaluate();
					try {
	
						if( result.hasNext()) {
							ret = new ArrayList<URI>();
							
							while (result.hasNext()) {
								ret.add( (URI)result.next().getValue("x") );
							}
						}
					} finally {
						result.close();
					}
				} finally {
					con.close();
				}
			}
			
		}catch (Exception e) {
			logger.error("Error occoured while selectSubjects: " ,e);

		}	
		
		return ret;		
	}

	
	public static ArrayList<URI> selectPropertiesOfOM( URI uri, URI type ) {
		ArrayList<URI> ret = null;

		try{
			RepositoryConnection con = getRepository().getConnection();
			
			try {
				String query = "";
				if( type!=null )
					query = "SELECT DISTINCT P FROM {P} <" + Vocabulary.DOMAIN + "> {C} <"+RDF.TYPE+"> {<" + Vocabulary.CLASS + ">}, {P} <" + Vocabulary.DOMAIN + "> {C} <" + Vocabulary.BELONGSTOONTOLOGYMODULE + "> {<" + uri.stringValue() + ">}, {P} <"+RDF.TYPE+"> {<" + type.stringValue() + ">}";
				else
					query = "SELECT DISTINCT P FROM {P} <" + Vocabulary.DOMAIN + "> {C} <"+RDF.TYPE+"> {<" + Vocabulary.CLASS + ">}, {P} <" + Vocabulary.DOMAIN + "> {C} <" + Vocabulary.BELONGSTOONTOLOGYMODULE + "> {<" + uri.stringValue()+ ">}";
			
				// TODO: Filter the ones, which are "hidden" from OntologyModule				
				
				TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
				TupleQueryResult result = tupleQuery.evaluate();
				try {

					if( result.hasNext()) {
						ret = new ArrayList<URI>();
						
						while (result.hasNext()) {
							ret.add( (URI)result.next().getValue("P") );
						}
					}
				} finally {
					result.close();
				}
			} finally {
				con.close();
			}
			
		}catch (Exception e) {
			logger.error("Error occoured while selectSubjects: ",e);
			logger.error(e);
		}	
		
		return ret;		
	}
	
	
	
	/**
	 * Selects the first subject, which have p in property and o in object (s p o)
	 * @param p	property
	 * @param o	object
	 * @return
	 */
	public static URI selectSubject( URI p, URI o ) {

		URI ret = null;

		try{
			RepositoryConnection con = getConnection();
			try {
				//select all ontologies


				if( p==null || o==null )
					return ret;
				
				String query = "SELECT DISTINCT x FROM {x} <" + p.stringValue() + "> {<" + o.stringValue() + ">} LIMIT 1";
				
				// "SELECT DISTINCT x FROM {x} <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> {type}; <http://www.w3.org/2000/01/rdf-schema#label> {l} WHERE type=<http://myontology.org/v0#Class> AND l LIKE "*bs*" IGNORE CASE LIMIT 10"

				//System.out.println(query);
				TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
				TupleQueryResult result = tupleQuery.evaluate();
				try {
					if( result.hasNext() ) {
						ret = (URI)result.next().getValue("x");
					}
				} finally {
					result.close();
				}
			} finally {
				con.close();
			}
			
		}catch (Exception e) {
			logger.error("Error occoured in selectSubject: ",e);

		}
		
		return ret;		
	}	/**
	 * Selects the first subject, which have p in property and o in object (s p o)
	 * @param p	property
	 * @param o	object
	 * @return
	 */
	public static URI selectSubject( URI p, String o ) {

		URI ret = null;

		try{
			RepositoryConnection con = getConnection();
			try {
				//select all ontologies

				String query = "SELECT DISTINCT x FROM {x} <" + p.stringValue() + "> {o} WHERE isLiteral(o) AND o=\"" + o + "\" LIMIT 1";
				
				// "SELECT DISTINCT x FROM {x} <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> {type}; <http://www.w3.org/2000/01/rdf-schema#label> {l} WHERE type=<http://myontology.org/v0#Class> AND l LIKE "*bs*" IGNORE CASE LIMIT 10"

				//System.out.println(query);
				TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
				TupleQueryResult result = tupleQuery.evaluate();
				try {
					if( result.hasNext() ) {
						ret = (URI)result.next().getValue("x");
					}
				} finally {
					result.close();
				}
			} finally {
				con.close();
			}
			
		}catch (Exception e) {
			logger.error("Error occoured in selectSubject: ",e);
			
		}
		
		return ret;		
	}
	
	
	
	/**
	 * Selects all objects, which have p in property and s in subject (s p o)
	 * @param p	property
	 * @param o	object
	 * @return
	 *//*
	public static ArrayList<URI> selectObjects( URI s, URI p ) {

		ArrayList<URI> ret = null;
		
		try{
			RepositoryConnection con = getRepository().getConnection();
			try {
				//select all ontologies

				String query = "SELECT DISTINCT x FROM {<" + s.stringValue() + ">} <" + p.stringValue() + "> {x}";
				//System.out.println( query );
				
				// "SELECT DISTINCT x FROM {x} <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> {type}; <http://www.w3.org/2000/01/rdf-schema#label> {l} WHERE type=<http://myontology.org/v0#Class> AND l LIKE "*bs*" IGNORE CASE LIMIT 10"

				//System.out.println(query);
				TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
				TupleQueryResult result = tupleQuery.evaluate();
				try {			
					if( result.hasNext()) {
						ret = new ArrayList<URI>();
						
						while (result.hasNext()) {
							ret.add( (URI)result.next().getValue("x") );
						}
					}
				} finally {
					result.close();
				}
			} finally {
				con.close();
			}
			
		}catch (Exception e) {
			System.out.println("Error occoured while selectSubjects: " + e.getMessage());
			e.printStackTrace();
		}		
		
		return ret;		
	}*/
	
	/**
	 * Selects all objects, which have p in property and s in subject (s p o); o has to have given type
	 * @param p	property
	 * @param o	object
	 * @param type type
	 * @return
	 */
	public static ArrayList<URI> selectObjects( URI s, URI p, URI type ) {

		ArrayList<URI> ret = null;

		try{
			RepositoryConnection con = getRepository().getConnection();
			try {
				//select all ontologies

				String query = "";

				if( s==null || p==null )
					return ret;
				
				if( type!=null)
					query = "SELECT DISTINCT x FROM {<" + s.stringValue() + ">} <" + p.stringValue() + "> {x} <"+RDF.TYPE+"> {<" + type.stringValue() + ">}";
				else
					query = "SELECT DISTINCT x FROM {<" + s.stringValue() + ">} <" + p.stringValue() + "> {x}";
					
				//System.out.println( query );
				
				// "SELECT DISTINCT x FROM {x} <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> {type}; <http://www.w3.org/2000/01/rdf-schema#label> {l} WHERE type=<http://myontology.org/v0#Class> AND l LIKE "*bs*" IGNORE CASE LIMIT 10"

				//System.out.println(query);
				TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
				TupleQueryResult result = tupleQuery.evaluate();
				try {			
					if( result.hasNext()) {
						ret = new ArrayList<URI>();
						
						while (result.hasNext()) {
							ret.add( (URI)result.next().getValue("x") );
						}
					}
				} finally {
					result.close();
				}
			} finally {
				con.close();
			}
			
		}catch (Exception e) {
			logger.error("Error occoured while selectSubjects: " ,e);
			
		}
		
		return ret;		
	}

	/**
	 * Selects all objects, which have p in property and s in subject (s p o); o has to have given type
	 * @param p	property
	 * @param o	object
	 * @param type type
	 * @return
	 */
	public static ArrayList<URI> selectObjects( URI s, URI p ) {

		ArrayList<URI> ret = selectObjects( s, p, null );
			
		return ret;
	}
	
	
	
	
	/**
	 * Selects the first triples' predicate of triples having s as subject and o as object
	 * @param s	subject
	 * @param p	property
	 * @return
	 * @throws Exception
	 */
	public static URI selectPredicate( URI s, URI o ) throws Exception {
		URI ret = null;

		try {
			RepositoryConnection con = getRepository().getConnection();

			try {				
				RepositoryResult<Statement> statements = con.getStatements( s, null, o, false );

				try { 
					if (statements.hasNext()) {
						ret = (URI)statements.next().getPredicate();
					}
				}
				finally {
					statements.close();
				}
				
			}
			finally {
				con.close();
			}
					
		} catch (Exception e) {
			logger.error("Error while selection a object: " + e.getMessage());
			e.printStackTrace();
		}
			
		return ret;
	}
	
	/**
	 * Selects the predicates of triples having s as subject and o as object
	 * @param s	subject
	 * @param p	property
	 * @return
	 * @throws Exception
	 */
	public static ArrayList<URI> selectPredicates( URI s, URI o ) throws Exception {
		ArrayList<URI> ret = null;

		try {
			RepositoryConnection con = getRepository().getConnection();

			try {				
				RepositoryResult<Statement> statements = con.getStatements( s, null, o, false );

				try { 
					while (statements.hasNext()) {
						if( ret == null )
							ret = new ArrayList<URI>();
						
						ret.add( (URI)statements.next().getPredicate() );
					}
				}
				finally {
					statements.close();
				}
				
			}
			finally {
				con.close();
			}
					
		} catch (Exception e) {
			logger.error("Error while selection a object: " + e.getMessage());
			
		}
			
		return ret;
	}
	
	
	/**
	 * Selects the first triples' object of triples having s as subject and p as property
	 * @param s	subject
	 * @param p	property
	 * @return
	 * @throws Exception
	 */
	public static URI selectObject( URI s, URI p ) throws Exception {
		URI ret = null;
			
		try{
			RepositoryConnection con = getRepository().getConnection();

			try {				
				RepositoryResult<Statement> statements = con.getStatements( s, p, null, false );

				try { 
					if (statements.hasNext()) {
						ret = (URI)statements.next().getObject();
					}
				}
				finally {
					statements.close();
				}
				
			}
			finally {
				con.close();
			}
					
		}catch (Exception e) {
			logger.error("Error while selection a object: " + e.getMessage());
			
		}
			
		return ret;
	}
	

	/**
	 * Selects the first triples' object of triples having s as subject and p as property
	 * @param s	subject
	 * @param p	property
	 * @return
	 * @throws Exception
	 */
	public static URI selectObject( URI s, URI p, URI type ) throws Exception {
		URI ret = null;

		try {
			RepositoryConnection con = getRepository().getConnection();

			try {				

				String query = "SELECT DISTINCT x FROM {<" + s + ">} <" + p + "> {x} <"+RDF.TYPE+"> {<" + type + ">} LIMIT 1";
				//System.out.println( query );
				
				// "SELECT DISTINCT x FROM {x} <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> {type}; <http://www.w3.org/2000/01/rdf-schema#label> {l} WHERE type=<http://myontology.org/v0#Class> AND l LIKE "*bs*" IGNORE CASE LIMIT 10"

				//System.out.println(query);
				TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
				TupleQueryResult result = tupleQuery.evaluate();
				try {			
					if( result.hasNext()) {
						ret = (URI)result.next().getValue("x");
					}
				} finally {
					result.close();
				}
				
			}
			finally {
				con.close();
			}
					
		}catch (Exception e) {
			logger.error("Error while selection a object: " + e.getMessage());
			
		}
			
		return ret;
	}	
	
	
	
	
	/**
	 * Selects the first triples' object of triples having s as subject and p as property
	 * @param s	subject
	 * @param p	property
	 * @return
	 * @throws Exception
	 */
	public static ArrayList<String> selectLiteralObjects(URI s, URI p, String lang) throws Exception {
		ArrayList<String> ret = null;

		try{
			RepositoryConnection con = getRepository().getConnection();	
			
			try {
				String query = "SELECT DISTINCT x FROM {<" + s.stringValue() + ">} <"+ p.stringValue()+"> {x} WHERE isLiteral(x)";
				
				if( lang!=null) {
					query += " AND lang(x)=\"" + lang + "\"";
				}
				
				 //System.out.println(query);
				TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
				TupleQueryResult result = tupleQuery.evaluate();

				try {
					if( result.hasNext()) {
						ret = new ArrayList<String>();
						
						while (result.hasNext()) {
							ret.add( result.next().getValue("x").stringValue() );
						}
					}
					
				} finally {
					result.close();
				}
			} finally {
				con.close();
			}
			
		} catch (Exception e) {
			logger.error("Error while selecting " + e.getMessage());
			return null;
		}
		
		return ret;
	}
	
	/**
	 * Selects the first triples' object of triples having s as subject and p as property
	 * @param s	subject
	 * @param p	property
	 * @param lang the language of the literal
	 * @return
	 * @throws Exception
	 */
	public static String selectLiteralObject(URI s, URI p, String lang) throws Exception {
		String ret = null;

		try {
			RepositoryConnection con = getRepository().getConnection();	
			
			try {
				String query = "SELECT DISTINCT x FROM {<" + s.stringValue() + ">} <"+ p.stringValue()+"> {x} WHERE isLiteral(x)";
				
				if( lang!=null) {
					query += " AND lang(x)=\"" + lang + "\"";
				}
				
				query += " LIMIT 1";
				
				 //System.out.println(query);
				TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
				TupleQueryResult result = tupleQuery.evaluate();

				try {
					
					if (result.hasNext()) {
						ret = result.next().getValue("x").stringValue();
					}
					
				} finally {
					result.close();
				}
			} finally {
				con.close();
			}
			
		} catch (Exception e) {
			logger.error("Error while selecting " + e.getMessage());
			return null;
		}
		
		return ret;
	}
	
	public static boolean insertProperty(URI s, URI p, String val) throws Exception {
		boolean ret = false;
		
		if( s!=null && p!=null ) {
			try {
				RepositoryConnection con = getConnection();
				
				try {
					con.add(s, p, new LiteralImpl(val) );
					
					con.commit();
					
					ret = true;
				} catch( Exception e ) {
					con.rollback();	
					throw e;
				} finally {
					con.close();
				}
			} catch (Exception e) {
				logger.error("Error while inserting a property" + e.getMessage());
				throw e;
			}
		}
			
		return ret;
	}
	
	public static boolean deleteProperty(URI s, URI p) throws Exception {
		boolean ret = true;
		try {
			RepositoryConnection con = getRepository().getConnection();
			
			try {

				/*
				//select the class with the url
				String query = "SELECT * FROM {<" + s.stringValue() + ">} <" + p.stringValue() + "> {x}";
				// System.out.println(query);
				TupleQuery tupleQuery = con.prepareTupleQuery(QueryLanguage.SERQL, query);
				TupleQueryResult result = tupleQuery.evaluate();
								
				try {
					while (result.hasNext()) {							
						con.remove(s, p, result.next().getValue("x"));
					}	
				} finally {
					result.close();
				}
				*/
				
				con.remove(s, p, null);
				
				con.commit();
			} catch( Exception e ) {
				con.rollback();	
				throw e;
			} finally {
				con.close();
			}
		} catch (Exception e) {
			logger.error("Error while deleting a property: " + e.getMessage());
			ret = false;
			
			throw e;
		}
			
		return ret;
	}
	
	

	public static void addTranslations( RepositoryConnection con, URI id, ArrayList<TranslationContainer> list ) throws Exception {
		if( list!=null) {								
			for (int i = 0; i<list.size(); i++) {
				con.add(id, Vocabulary.TRANSLATION, new LiteralImpl(list.get(i).getValue(), list.get(i).getLanguage()));
			}
		}
	}
	
	public static void addLiterals( RepositoryConnection con, URI id, URI property, ArrayList<String> list ) throws Exception {
		if( list!=null) {								
			for (int i = 0; i<list.size(); i++) {
				con.add(id, property, new LiteralImpl(list.get(i)));
			}
		}
	}
	
	public static void addURIs( RepositoryConnection con, URI subject, URI predicate, ArrayList<URI> objects ) throws Exception {
		if( objects!=null ) {								
			for( int i=0; i<objects.size(); i++ ) {
				con.add(subject, predicate, objects.get(i) );
			}
		}
	}


	
	

	
	
	
	// has to be overwritten:
	/*
	public static Object select( URI uri, String lang) {
		return null;
	}
	*/
	
	// has to be overwritten:
	/*
	public static boolean update( URI uri, Object data, String lang) {
		return false;
	}
	*/
	
	
	

	// ##################################

	/**
	 * Returns an ArrayList containing all URIs with the given type
	 */
	public static ArrayList<URI> selectAllUris( URI type ) {
		
		ArrayList<URI> ret = null;	
	
		try {

			RepositoryConnection con = getConnection();
			try {				
				RepositoryResult<Statement> statements = con.getStatements( null, RDF.TYPE, type, false );

				try { 
					if( statements.hasNext()) {
						
						ret = new ArrayList<URI>();
						
						while (statements.hasNext()) {
							
							ret.add( (URI)statements.next().getSubject() );
						}
					}
				}
				finally {
					statements.close();
				}
				
			}
			finally {
				con.close();
			}
					
		}catch (Exception e) {
			logger.error("Error occoured while selecting all URIs:" + e.getMessage());
			
		}	
		
		return ret;		
	}

	/**
	 * Selects all what is stored in Translations (selectTranslation will also generate some meaninful for archived versions)
	 * @param uri
	 * @return
	 * @throws Exception
	 */
	public static ArrayList<TranslationContainer> selectTranslationsPlain( URI s ) throws Exception {

		ArrayList<TranslationContainer> ret = null;

		RepositoryConnection con = getRepository().getConnection();				
		try {		
			RepositoryResult<Statement> statements = con.getStatements( s, Vocabulary.TRANSLATION, null, false );
			try {
				
				if( statements.hasNext()) {
					ret = new ArrayList<TranslationContainer>();
					
					while (statements.hasNext()) {
						Statement st = statements.next();
						Literal l = (Literal)st.getObject();
						ret.add( new TranslationContainer(l.getLabel(), l.getLanguage() ) );
					}
				}
			} finally {
				statements.close();
			}	
		} finally {
			con.close();
		}
		
		return ret;
	}

	public static void close() {
		try {
			if( myRepository != null ) {
				myRepository.shutDown();
			}
		}
		catch (Exception e) {
			logger.error("problem closing repository",e);
		}
	}
	
}
